package com.hnas.core.app_common.util;



import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.DESedeKeySpec;




/**
 * Created by cq on 2015/3/17.
 */
public class RSATool {
	 	private final static String DES = "DES";  
	    private final static String CIPHER_ALGORITHM = "DES/ECB/NoPadding";
	    private final static String TriDes = "DESede/ECB/NoPadding";  
	    static byte[] key_all={(byte) 0x06,(byte) 0x13,(byte) 0x12,(byte) 0x23,(byte) 0x92,(byte) 0x98,(byte) 0x77,(byte) 0x39};
	    /** 
	     * DES 加密 
	     *  
	     * @param src 数据源 
	     * @param key 密钥，长度必须是8的倍数  用的其实只是8字节的秘钥
	     * @return 返回加密后的数据 
	     * @throws DesException 
	     */  
	    public static byte[] encrypt(byte[] src, byte[] key) {  
	        // DES算法要求有一个可信任的随机数源  
	        SecureRandom sr = new SecureRandom();  
	        try {  
	            // 从原始密匙数据创建DESKeySpec对象  
	            DESKeySpec dks = new DESKeySpec(key);  
	            // 创建一个密匙工厂，然后用它把DESKeySpec转换成  
	            // 一个SecretKey对象  
	            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES);  
	            SecretKey securekey = keyFactory.generateSecret(dks);  
	            // Cipher对象实际完成加密操作,NoPadding为填充方式 默认为PKCS5Padding  
	            Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);  
	            // 用密匙初始化Cipher对象  
	            cipher.init(Cipher.ENCRYPT_MODE, securekey, sr);  
	            // 现在，获取数据并加密  
	            // 正式执行加密操作  
	            return cipher.doFinal(src);  
	        } catch (NoSuchAlgorithmException e) {  
	        	int x=0;
	             //LogUtil.getInstance().errorrror("算数错误", e);  
	        } catch (InvalidKeyException e) {  
	            // LogUtil.getInstance().errorrror("无效key错误", e);  
	        	int x=0;
	        } catch (InvalidKeySpecException e) {  
	            // LogUtil.getInstance().errorrror("无效key戳无", e); 
	        	int x=0;
	        } catch (NoSuchPaddingException e) {  
	            // LogUtil.getInstance().errorrror("填充错误", e);  
	        	int x=0;
	        } catch (IllegalBlockSizeException e) {  
	            // LogUtil.getInstance().errorrror("非法数据块", e); 
	        	int x=0;
	        } catch (BadPaddingException e) {  
	            // LogUtil.getInstance().errorrror("错误的填充", e);  
	        	int x=0;
	        }  
	        return null;  
	    }  
	  
	    /** 
	     * 生成密钥 
	     *  
	     * @return 
	     * @throws NoSuchAlgorithmException 
	     */  
	    public static byte[] initKey() throws NoSuchAlgorithmException {  
	        KeyGenerator kg = KeyGenerator.getInstance(DES);  
	        kg.init(56);  
	        SecretKey secretKey = kg.generateKey();  
	        return secretKey.getEncoded();  
	    }  
	  
	    /** 
	     * DES解密 
	     *  
	     * @param src 数据源 
	     * @param key 密钥，长度必须是8的倍数    用的其实只是8字节的秘钥
	     * @return 返回解密后的原始数据 
	     * @throws DesException 
	     * @throws Exception 
	     */  
	    public static byte[] decrypt(byte[] src, byte[] key) {  
	        // DES算法要求有一个可信任的随机数源  
	        SecureRandom sr = new SecureRandom();  
	        try {  
	            // 从原始密匙数据创建一个DESKeySpec对象  
	            DESKeySpec dks = new DESKeySpec(key);  
	            // 创建一个密匙工厂，然后用它把DESKeySpec对象转换成  
	            // 一个SecretKey对象  
	            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES);  
	            SecretKey securekey = keyFactory.generateSecret(dks);  
	            // Cipher对象实际完成解密操作  
	            Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);  
	            // 用密匙初始化Cipher对象  
	            cipher.init(Cipher.DECRYPT_MODE, securekey, sr);  
	            // 现在，获取数据并解密  
	            // 正式执行解密操作  
	            return cipher.doFinal(src);  
	        } catch (NoSuchAlgorithmException e) {  
//	            LogUtil.getInstance().errorrror("算数错误", e);  
	        } catch (InvalidKeyException e) {  
//	            LogUtil.getInstance().errorrror("无效key错误", e);  
	        } catch (InvalidKeySpecException e) {  
//	            LogUtil.getInstance().errorrror("无效key戳无", e);  
	        } catch (NoSuchPaddingException e) {  
//	            LogUtil.getInstance().errorrror("填充错误", e);  
	        } catch (IllegalBlockSizeException e) {  
//	            LogUtil.getInstance().errorrror("非法数据块", e);  
	        } catch (BadPaddingException e) {  
//	            LogUtil.getInstance().errorrror("错误的填充", e);  
	        }  
	        return null;  
	    }  
	    /******************
	     * 3DES 加密  
	     * @param data  输入数据  数据不为8的倍数，函数内部自动补0到8的倍数
	     * @param key	输入秘钥 16字节秘钥
	     * @return 输出加密后数据
	     *************/
	    public static byte[] trides_crypt(byte data[],byte key[]) {  
	        try {  
	            byte[] k = new byte[24];  
	  
	            int len = data.length;  
	            if(data.length % 8 != 0){  
	                len = data.length - data.length % 8 + 8;  
	            }  
	            byte [] needData = null;  
	            if(len != 0)  
	                needData = new byte[len];  
	              
	            for(int i = 0 ; i< len ; i++){  
	                needData[i] = 0x00;  
	            }  
	              
	            System.arraycopy(data, 0, needData, 0, data.length);  
	              
	            if (key.length == 16) {  
	                System.arraycopy(key, 0, k, 0, key.length);  
	                System.arraycopy(key, 0, k, 16, 8);  
	            } else {  
	                System.arraycopy(key, 0, k, 0, 24);  
	            }  
	  
	            KeySpec ks = new DESedeKeySpec(k);  
	            SecretKeyFactory kf = SecretKeyFactory.getInstance("DESede");  
	            SecretKey ky = kf.generateSecret(ks);  
	  
	            Cipher c = Cipher.getInstance(TriDes);  
	            c.init(Cipher.ENCRYPT_MODE, ky);  
	            return c.doFinal(needData);  
	        } catch (Exception e) {  
	            e.printStackTrace();  
	            return null;  
	        }  
	  
	    }  
	    /**************
	     * 3DES 解密
	     * @param data 输入数据  数据不为8的倍数，函数内部自动补0到8的倍数
	     * @param key 16字节秘钥
	     * @return
	     */
	    public static byte[] trides_decrypt(byte data[],byte key[]) {  
	        try {  
	            byte[] k = new byte[24];  

	            int len = data.length;  
	            if(data.length % 8 != 0){  
	                len = data.length - data.length % 8 + 8;  
	            }  
	            byte [] needData = null;  
	            if(len != 0)  
	                needData = new byte[len];  
	              
	            for(int i = 0 ; i< len ; i++){  
	                 needData[i] = 0x00;  
	             }  
	               
	             System.arraycopy(data, 0, needData, 0, data.length);  
	               
	             if (key.length == 16) {  
	                 System.arraycopy(key, 0, k, 0, key.length);  
	                 System.arraycopy(key, 0, k, 16, 8);  
	             } else {  
	                 System.arraycopy(key, 0, k, 0, 24);  
	             }  
	             KeySpec ks = new DESedeKeySpec(k);  
	             SecretKeyFactory kf = SecretKeyFactory.getInstance("DESede");  
	             SecretKey ky = kf.generateSecret(ks);  
	   
	             Cipher c = Cipher.getInstance(TriDes);  
	             c.init(Cipher.DECRYPT_MODE, ky);  
	             return c.doFinal(needData);  
	         } catch (Exception e) {  
	             e.printStackTrace();  
	             return null;  
	         }  
	   
	     }  
	    
	    /////////////////////////////////////
	    
	    public static byte[] IV = new byte[8];

		public static byte byteXOR(byte src, byte src1) {
			return (byte) ((src & 0xFF) ^ (src1 & 0xFF));
		}

		public static byte[] bytesXOR(byte[] src, byte[] src1) {
			int length = src.length;
			if (length != src1.length) {
				return null;
			}
			byte[] result = new byte[length];
			for (int i = 0; i < length; i++) {
				result[i] = byteXOR(src[i], src1[i]);
			}
			return result;
		}

		/**
		 * mac计算,数据不为8的倍数，需要补0，将数据8个字节进行异或，再将异或的结果与下一个8个字节异或，一直到最后，将异或后的数据进行DES计算
		 * 
		 * @param key
		 * @param Input
		 * @return
		 */
		public static byte[] clacMac(byte[] key, byte[] Input) {
			int length = Input.length;
			int x = length % 8;
			int addLen = 0;
			if (x != 0) {
				addLen = 8 - length % 8;
			}
			int pos = 0;
			byte[] data = new byte[length + addLen];
			System.arraycopy(Input, 0, data, 0, length);
			byte[] oper1 = new byte[8];
			System.arraycopy(data, pos, oper1, 0, 8);
			pos += 8;
			for (int i = 1; i < data.length / 8; i++) {
				byte[] oper2 = new byte[8];
				System.arraycopy(data, pos, oper2, 0, 8);
				byte[] t = bytesXOR(oper1, oper2);
				oper1 = t;
				pos += 8;
			}
			// 将异或运算后的最后8个字节（RESULT BLOCK）转换成16个HEXDECIMAL：
			byte[] resultBlock = bytesToHexString(oper1).getBytes();
			// 取前8个字节用mkey1，DES加密
			byte[] front8 = new byte[8];
			System.arraycopy(resultBlock, 0, front8, 0, 8);
			byte[] behind8 = new byte[8];
			System.arraycopy(resultBlock, 8, behind8, 0, 8);
			byte[] desfront8 = encrypt(front8, key);
			// 将加密后的结果与后8 个字节异或：
			byte[] resultXOR = bytesXOR(desfront8, behind8);
			// 用异或的结果TEMP BLOCK 再进行一次单倍长密钥算法运算
			byte[] buff = encrypt(resultXOR, key);
			// 将运算后的结果（ENC BLOCK2）转换成16 个HEXDECIMAL asc
			// 取8个长度字节
			byte[] retBuf = new byte[8];
			System.arraycopy(bytesToHexString(buff).getBytes(), 0, retBuf, 0, 8);
			return retBuf;
		}
		
		public static byte[] clacMac(byte[] Input) {
			int length = Input.length;
			int x = length % 8;
			int addLen = 0;
			if (x != 0) {
				addLen = 8 - length % 8;
			}
			int pos = 0;
			byte[] data = new byte[length + addLen];
			System.arraycopy(Input, 0, data, 0, length);
			byte[] oper1 = new byte[8];
			System.arraycopy(data, pos, oper1, 0, 8);
			pos += 8;
			for (int i = 1; i < data.length / 8; i++) {
				byte[] oper2 = new byte[8];
				System.arraycopy(data, pos, oper2, 0, 8);
				byte[] t = bytesXOR(oper1, oper2);
				oper1 = t;
				pos += 8;
			}
			// 将异或运算后的最后8个字节（RESULT BLOCK）转换成16个HEXDECIMAL：
			byte[] resultBlock = bytesToHexString(oper1).getBytes();
			// 取前8个字节用mkey1，DES加密
			byte[] front8 = new byte[8];
			System.arraycopy(resultBlock, 0, front8, 0, 8);
			byte[] behind8 = new byte[8];
			System.arraycopy(resultBlock, 8, behind8, 0, 8);
			byte[] desfront8 = encrypt(front8, key_all);
			// 将加密后的结果与后8 个字节异或：
			byte[] resultXOR = bytesXOR(desfront8, behind8);
			// 用异或的结果TEMP BLOCK 再进行一次单倍长密钥算法运算
			byte[] buff = encrypt(resultXOR, key_all);
			// 将运算后的结果（ENC BLOCK2）转换成16 个HEXDECIMAL asc
			// 取8个长度字节
			byte[] retBuf = new byte[8];
			System.arraycopy(bytesToHexString(buff).getBytes(), 0, retBuf, 0, 8);
			return retBuf;
		}
		
		public static final String bytesToHexString(byte[] bArray) {
			StringBuffer sb = new StringBuffer(bArray.length);
			String sTemp;
			for (int i = 0; i < bArray.length; i++) {
				sTemp = Integer.toHexString(0xFF & bArray[i]);
				if (sTemp.length() < 2)
					sb.append(0);
				sb.append(sTemp.toUpperCase());
			}
			return sb.toString();
		}
		/////////////////////////////////////////////////////////////////////////////////////////////////
		/*
		 * PIN加密
		 */
		/*
		 * 加密算法：
			1、计算出明文PIN的长度，并将补足长度为14位，后补F，如：1111111FFFFFFF；
			2、将卡号去掉最后一位检验位后，进行截取最后12位；
			3、将密码的2位长度+补好F的密码明文和截取后的卡号进行异或计算；
			4、将异或结果和明文的密钥进行DES计算得出密文。 
			
			解密算法：
			
			1、使用密文与密钥的明文进行DES解密；
			2、将卡号去掉最后一位检验位后，进行截取最后12位；
			3、将截取后的卡号与解密出来的明文进行异或计算；
			4、异或结果去掉补位的F即位明文的密码。 
		 */
		 /**
	     * 
	     * @param src0
	     * @param src1
	     * @return
	     */
	    private static byte uniteBytes(byte src0, byte src1) {
	         byte _b0 = Byte.decode("0x" + new String(new byte[] { src0 })).byteValue();
	         _b0 = (byte) (_b0 << 4);
	         byte _b1 = Byte.decode("0x" + new String(new byte[] { src1 })).byteValue();
	         byte ret = (byte) (_b0 ^ _b1);
	         return ret;
	     }

	    
		   /**
	     *  对pin进行加密
	     * @param pin
	     * @return
	     */
	    private static byte[] getHPin(String pin) {
	         
	         byte arrPin[] = pin.getBytes();
	         byte encode[] = new byte[8];
	         encode[0] = (byte) 0x06;
	         encode[1] = (byte) uniteBytes(arrPin[0], arrPin[1]);
	         encode[2] = (byte) uniteBytes(arrPin[2], arrPin[3]);
	         encode[3] = (byte) uniteBytes(arrPin[4], arrPin[5]);
	         encode[4] = (byte) 0xFF;
	         encode[5] = (byte) 0xFF;
	         encode[6] = (byte) 0xFF;
	         encode[7] = (byte) 0xFF;
	         return encode;
	     }
	    /**
	     * PIN加密的主帐号
	     * @param accno
	     * @return
	     */
	    private static byte[] getHAccno(String accno) {
	         int len = accno.length();
	         byte arrTemp[] = accno.substring(len < 13 ? 0 : len - 13, len - 1).getBytes();
	         byte arrAccno[] = new byte[12];
	         for (int i = 0; i < 12; i++) {
	           arrAccno[i] = (i <= arrTemp.length ? arrTemp[i] : (byte) 0x00);
	         }
	         byte encode[] = new byte[8];
	         encode[0] = (byte) 0x00;
	         encode[1] = (byte) 0x00;
	         encode[2] = (byte) uniteBytes(arrAccno[0], arrAccno[1]);
	         encode[3] = (byte) uniteBytes(arrAccno[2], arrAccno[3]);
	         encode[4] = (byte) uniteBytes(arrAccno[4], arrAccno[5]);
	         encode[5] = (byte) uniteBytes(arrAccno[6], arrAccno[7]);
	         encode[6] = (byte) uniteBytes(arrAccno[8], arrAccno[9]);
	         encode[7] = (byte) uniteBytes(arrAccno[10], arrAccno[11]);
	         return encode;
	     }

		 /**
	     * PIN BLOCK （PIN按位异或主帐号PAN）
	     * @param pin 密码
	     * @param accno 账号
	     * /**
	 *     ANSI X9.8 Format（带主帐号信息）
	*    PIN BLOCK 格式：等于 PIN 按位异或主帐号
	    PIN 格式：（与1中的格式类似）
	    Byte 1 PIN的长度
	    Byte 2 – Byte 3/4/5/6/7 4--12个PIN(每个PIN占4个BIT)
	    Byte4/5/6/7/8 – Byte 8 FILLER “F” (每个“F“占4个BIT)

	    PAN（主帐号 Primary Account Number）同样包含8个byte，格式如下：
	    Byte 1 — Byte 2 0x00 0x00
	    Byte 3 — Byte 8 12个主帐号字符（最后一位为校验位）
	    12位字符主帐号的取法：取主帐号的右12位（不包括最右边的校验位），不足12位左补“0X00”。
	 * @author yangw@eastcom.com
	 * @return
	 * */
	     public static byte[] generateProcess(String pin, String accno) {
	    	 byte arrPin[] = getHPin(pin);
	    	 
	         byte arrAccno[] = getHAccno(accno);
	         byte arrRet[] = new byte[8];
	         //PIN BLOCK 格式等于 PIN 按位异或 主帐号;
	         for (int i = 0; i < 8; i++) {
	           arrRet[i] = (byte) (arrPin[i] ^ arrAccno[i]);
	         }
	        
	         return arrRet;
	     }
	     public static byte[] decodeProcess(String pin, String accno)
	     {
	    	 byte arrPin[] = Util.String2BCD(pin, pin.length());
	         byte arrAccno[] = getHAccno(accno);
	         byte arrRet[] = new byte[8];
	         //PIN BLOCK 格式等于 PIN 按位异或 主帐号;
	         for (int i = 0; i < 8; i++) {
	           arrRet[i] = (byte) (arrPin[i] ^ arrAccno[i]);
	         }
	        
	         return arrRet;
	     }
		 /**
	      * 对账号和密码进行加密，生成加密后的密码
	      * pinkey  16位
	      * @param accNo  账号或者卡号--实际只取该参数去掉最后一位后，取最后12位。
	      * @param passwd 密码
	      * @return 
	      * @throws Exception 
	      */
	     public static byte[] generatePasswd(byte[] pinkey,String accNo,String passwd) throws Exception{
	         
	        byte[] pinblock = generateProcess(passwd,accNo);
	        return trides_crypt(pinblock,pinkey);
	        
	     }
	     /***
	      * 对账号和密码进行解密，生成解密后的密码
	      * @param pinkey 
	      * @param accNo  分散因子
	      * @param strPasswd 密码密文
	      * @return
	      * @throws Exception
	      */
	     public static String decodePasswd(byte[] pinkey,String accNo,String strPasswd) throws Exception
	     {
	        byte[] passwd = Util.String2BCD(strPasswd, strPasswd.length());
	        byte[] pinblock=trides_decrypt(passwd,pinkey);
	        String strPinBlock=Util.BCD2String(pinblock, pinblock.length);
	        byte[] ret=decodeProcess(strPinBlock, accNo);
	        return Util.BCD2String(ret, ret.length).substring(2, 8);
	        
	     }
		
}